/**
 *******************************************************************************
 * @file    tiny_kvdb_port.c
 * @brief   port functions for tiny kvdb.
 *******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "tiny_kvdb.h"
#include "flash.h"
#include "string.h"

/* Private Defines -----------------------------------------------------------*/
#define FLASH_BYPASS()              M0P_FLASH->BYPASS_f.BYSEQ = 0x5A5A;\
                                    M0P_FLASH->BYPASS_f.BYSEQ = 0xA5A5;

#define SECTOR_SIZE                 512

/* Private TypeDefs ----------------------------------------------------------*/
typedef enum en_flash_lock
{
    LockAll   = 0x0000u,      
    UnlockAll = 0xFFFFu,     
} en_flash_lock_t;

/* Private Macros ------------------------------------------------------------*/

/* Private Variables ---------------------------------------------------------*/

/* Extern Variables ----------------------------------------------------------*/

/* Private FunctionPrototypes ------------------------------------------------*/

/* Extern FunctionPrototypes -------------------------------------------------*/

/* Private Functions ---------------------------------------------------------*/
int flash_init (void)
{
    return 1;
}

int flash_read (uint32_t addr, void * p_dst, uint32_t len)
{
    uint8_t *p = p_dst;
    for (uint32_t i = 0; i < len; i++, addr++, p++)
    {
        *p = *(uint8_t *) addr;
    }

    return len;
}

int flash_write (uint32_t addr, void * p_src, uint32_t len)
{
    uint32_t write_addr = addr;
    uint32_t data_write;
    const uint8_t *p = (uint8_t *)p_src;

    __disable_irq();

    /* address not word-aligned */
    if (write_addr & 0x03)
    {
        if (!(write_addr & 0x02))
        {
            data_write = *p + ((uint16_t)*(p+1) << 8);
            Flash_WriteHalfWord(write_addr, (uint16_t)data_write);
            write_addr += 2;
            len -= 2;
            p += 2;
        }
        if ((write_addr & 0x01))
        {
            data_write = *p;
            Flash_WriteHalfWord(write_addr, (uint8_t)data_write);
            write_addr += 1;
            len -= 1;
            p += 1;
        }
    }

    if (len >= 4)
    {
        /* unlock flash */
        FLASH_BYPASS();
        M0P_FLASH->SLOCK_f.SLOCK = UnlockAll;
        while (TRUE == M0P_FLASH->CR_f.BUSY)
        {
            ;
        }
        /* set OP */
        FLASH_BYPASS();
        M0P_FLASH->CR_f.OP = 1;

        while(len >= 4)
        {
            /* source address is word-aligned */
            if (((uint32_t)p & 0x03) == 0)
            {
                data_write = *(uint32_t *)p;
            }
            else
            {
                data_write = ((uint32_t)*(p+3) << 24) + ((uint32_t)*(p+2) << 16) + ((uint32_t)*(p+1) << 8) + *p;
            }

            /* write data */
            *((volatile uint32_t*)write_addr) = data_write;
            while (TRUE == M0P_FLASH->CR_f.BUSY)
            {
                ;
            }

            write_addr += 4;
            len -= 4;
            p += 4;
        }

        /* lock flash */
        FLASH_BYPASS();
        M0P_FLASH->SLOCK_f.SLOCK = LockAll;
    }
    if (len >= 2)
    {
        data_write = *p + ((uint16_t)*(p+1) << 8);
        Flash_WriteHalfWord(write_addr, (uint16_t)data_write);
        write_addr += 2;
        len -= 2;
        p += 2;
    }
    if (len != 0)    /* len == 1 */
    {
        Flash_WriteByte(write_addr, *(uint8_t *)p);
    }

    __enable_irq();

    return len;
}

int flash_erase (uint32_t addr, uint32_t len)
{
    uint32_t start_addr = addr;
    uint32_t end_addr = start_addr + len;
    uint32_t sec_start_addr = start_addr & ~(SECTOR_SIZE - 1);

    __disable_irq();

    while (sec_start_addr < end_addr)
    {
        Flash_SectorErase(sec_start_addr);
        sec_start_addr += SECTOR_SIZE;
    }

    __enable_irq();

    return len;
}

tiny_db_device_t hc32l110_onchip_flash =
{
    .device_id 	= 0x48434C31,   /* HCL1 */
    .start_addr = 0x0,
    .max_size = 32*1024,
    .sector_size= 512,
    .write_unit = 2,        
    
    .init = flash_init,
    .read = flash_read,
    .write = flash_write,
    .erase = flash_erase
};


/******************************** END OF FILE *********************************/


